For the IWPR Q1 Update (Jan 2025)

Query and load IM3 data

Run this query_im3_scen("energy") only once to query from remote IM3 databases. Once a .dat file is created, we can load the existing project data by loadProject(proj = "im3scen_energy.dat").

# query the data
# im3_energy <- query_im3_scen("energy")
# load the data
im3_energy <- loadProject(proj = paste0("../", data_dir, "im3scen_energy.dat"))
# scenarios and queries 
listScenarios(im3_energy)
[1] "rcp45cooler_ssp3" "rcp45cooler_ssp5" "rcp45hotter_ssp3" "rcp45hotter_ssp5" "rcp85cooler_ssp3" "rcp85cooler_ssp5" "rcp85hotter_ssp3" "rcp85hotter_ssp5"
listQueries(im3_energy)
[1] "USA inputs by tech"                 "USA outputs by tech"                "inputs by subsector (non-electric)" "elec gen by subsector"              "USA regional natural gas outputs"   "elec energy input by subsector"    
# mappings 
source_mapping <- read_csv(paste0("../", data_dir, "mappings/source_mapping_en.csv"))
Rows: 85 Columns: 2-- Column specification -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): input, Source
i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
target_mapping <- read_csv(paste0("../", data_dir, "mappings/target_mapping_en.csv"))
Rows: 102 Columns: 2-- Column specification -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): sector, Target
i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
node_mapping <- read_csv(paste0("../", data_dir, "mappings/node_mapping_en.csv")) 
Rows: 20 Columns: 5-- Column specification -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (4): label, stage, hex, color_name
dbl (1): node
i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.

Energy Sankey

# get queries 
inputByTechUSA <- getQuery(im3_energy, "USA inputs by tech") 
outputByTechUSA <- getQuery(im3_energy, "USA outputs by tech")

inputBySubsectorNonElec <- getQuery(im3_energy, 'inputs by subsector (non-electric)')
elecEnergyInputBySubsector <- getQuery(im3_energy, 'elec energy input by subsector') %>% filter(Units == "EJ") # in case no filtering of ELEC_RPS credits
elecGenBySubsector <- getQuery(im3_energy, 'elec gen by subsector') %>% filter(Units == "EJ") # in case no filtering of ELEC_RPS credits
natGasOutputs <- getQuery(im3_energy, 'USA regional natural gas outputs')
datatables_energy <- list(
  "inputByTechUSA" = inputByTechUSA,
  "outputByTechUSA" = outputByTechUSA,
  "inputBySubsectorNonElec" = inputBySubsectorNonElec,
  "elecEnergyInputBySubsector" = elecEnergyInputBySubsector,
  "elecGenBySubsector" = elecGenBySubsector,
  "natGasOutputs" = natGasOutputs
)

# print column names of each datatable
lapply(datatables_energy, function(x) colnames(x))
$inputByTechUSA
[1] "Units"      "scenario"   "region"     "sector"     "subsector"  "technology" "input"      "year"       "value"     

$outputByTechUSA
[1] "Units"      "scenario"   "region"     "sector"     "subsector"  "technology" "output"     "year"       "value"     

$inputBySubsectorNonElec
[1] "Units"     "scenario"  "region"    "sector"    "subsector" "input"     "year"      "value"    

$elecEnergyInputBySubsector
[1] "Units"     "scenario"  "region"    "sector"    "subsector" "input"     "year"      "value"    

$elecGenBySubsector
[1] "Units"     "scenario"  "region"    "subsector" "year"      "value"    

$natGasOutputs
[1] "Units"      "scenario"   "region"     "sector"     "technology" "output"     "year"       "value"     
# print the first few rows of each datatable
lapply(datatables_energy, function(x) (x))
$inputByTechUSA

$outputByTechUSA

$inputBySubsectorNonElec

$elecEnergyInputBySubsector

$elecGenBySubsector

$natGasOutputs
NA

Let’s process each piece to prepare the format of: scenario, source, target, year, value. Scenario and year could be filtered for each Sankey.

Non-electricity

# map non electricity energy flows to major aggregated categories based on the mapping file 


inputs_by_subsector_nonelec <- inputBySubsectorNonElec %>% 
  filter(Units == 'EJ') %>%
  filter(!input %in% c('regional corn', 'regional soybean')) %>% 
  # aggregate all monthly_day combinations to one category e.g., electricity domestic supply_Nov_day to electricity domestic supply
  remove_month_day_night_superpeak("sector") %>% remove_month_day_night_superpeak("input") %>%
  left_join(source_mapping, by = 'input') %>%
  left_join(target_mapping, by = 'sector')

# Note there are NAs in the output due to missing mappings or sectors that are
# not supposed to be targets and inputs that are not supposed# to be sources
# things that were remapped as sources
unique((inputs_by_subsector_nonelec %>% filter(!is.na(Source)))$sector)
 [1] "comm cooking"          "comm cooling"          "comm heating"          "comm hot water"        "comm lighting"         "comm non-building"     "comm office"           "comm other"            "comm refrigeration"    "comm ventilation"      "delivered biomass"     "gas to liquids"        "industrial energy use"
[14] "industrial feedstocks" "industry"              "oil refining"          "regional biomass"      "resid clothes dryers"  "resid clothes washers" "resid computers"       "resid cooking"         "resid dishwashers"     "resid freezers"        "resid furnace fans"    "resid heating"         "resid hot water"      
[27] "resid lighting"        "resid other"           "resid refrigerators"   "resid televisions"     "trn_aviation_intl"     "trn_freight"           "trn_freight_road"      "trn_pass"              "trn_pass_road"         "trn_pass_road_LDV"     "trn_pass_road_LDV_4W"  "trn_shipping_intl"     "biomass liquids"      
[40] "cement"                "coal to liquids"       "process heat cement"   "regional biomassOil"   "N fertilizer"         
unique((inputs_by_subsector_nonelec %>% filter(!is.na(Source)))$subsector)
 [1] "electricity"            "gas"                    "biomass"                "coal"                   "refined liquids"        "delivered biomass"      "gas to liquids"         "hydrogen"               "industry"               "oil refining"           "regional biomass"       "International Aviation" "Domestic Ship"         
[14] "Freight Rail"           "Heavy truck"            "Light truck"            "Medium truck"           "Domestic Aviation"      "HSR"                    "Passenger Rail"         "Bus"                    "2W and 3W"              "Car"                    "Large Car and Truck"    "International Ship"     "biomass liquids"       
[27] "cement"                 "coal to liquids"        "regional biomassOil"   
unique((inputs_by_subsector_nonelec %>% filter(!is.na(Source)))$input) # look at this
 [1] "elect_td_bld"               "delivered gas"              "delivered biomass"          "delivered coal"             "refined liquids enduse"     "regional biomass"           "regional natural gas"       "elect_td_ind"               "wholesale gas"              "H2 enduse"                  "refined liquids industrial"
[12] "industrial energy use"      "industrial feedstocks"      "industrial processes"       "regional oil"               "elect_td_trn"               "regional biomassOil"        "regional corn for ethanol"  "regional coal"              "regional oilcrop"          
# things there were NOT mapped as sources 
unique((inputs_by_subsector_nonelec %>% filter(is.na(Source)))$sector)
[1] "comm cooling"          "comm heating"          "elect_td_ind"          "elect_td_trn"          "industrial feedstocks" "resid cooling"         "resid heating"         "trn_pass"              "cement"               
unique((inputs_by_subsector_nonelec %>% filter(is.na(Source)))$subsector)
[1] "electricity"     "elect_td_ind"    "elect_td_trn"    "refined liquids" "Cycle"           "Walk"            "cement"         
unique((inputs_by_subsector_nonelec %>% filter(is.na(Source)))$input) # look at this 
[1] "electricity domestic supply" "oil-credits"                 "renewable"                   "process heat cement"        
# things that were remapped as targets
unique((inputs_by_subsector_nonelec %>% filter(!is.na(Target)))$sector) # look at this
 [1] "comm cooking"          "comm cooling"          "comm heating"          "comm hot water"        "comm lighting"         "comm non-building"     "comm office"           "comm other"            "comm refrigeration"    "comm ventilation"      "delivered biomass"     "elect_td_ind"          "elect_td_trn"         
[14] "gas to liquids"        "industrial energy use" "industrial feedstocks" "industry"              "oil refining"          "regional biomass"      "resid clothes dryers"  "resid clothes washers" "resid computers"       "resid cooking"         "resid cooling"         "resid dishwashers"     "resid freezers"       
[27] "resid furnace fans"    "resid heating"         "resid hot water"       "resid lighting"        "resid other"           "resid refrigerators"   "resid televisions"     "trn_aviation_intl"     "trn_freight"           "trn_freight_road"      "trn_pass"              "trn_pass_road"         "trn_pass_road_LDV"    
[40] "trn_pass_road_LDV_4W"  "trn_shipping_intl"     "biomass liquids"       "cement"                "coal to liquids"       "process heat cement"   "regional biomassOil"   "N fertilizer"         
unique((inputs_by_subsector_nonelec %>% filter(!is.na(Target)))$subsector)
 [1] "electricity"            "gas"                    "biomass"                "coal"                   "refined liquids"        "delivered biomass"      "elect_td_ind"           "elect_td_trn"           "gas to liquids"         "hydrogen"               "industry"               "oil refining"           "regional biomass"      
[14] "International Aviation" "Domestic Ship"          "Freight Rail"           "Heavy truck"            "Light truck"            "Medium truck"           "Cycle"                  "Domestic Aviation"      "HSR"                    "Passenger Rail"         "Walk"                   "Bus"                    "2W and 3W"             
[27] "Car"                    "Large Car and Truck"    "International Ship"     "biomass liquids"        "cement"                 "coal to liquids"        "regional biomassOil"   
unique((inputs_by_subsector_nonelec %>% filter(!is.na(Target)))$input)
 [1] "elect_td_bld"                "delivered gas"               "electricity domestic supply" "delivered biomass"           "delivered coal"              "refined liquids enduse"      "regional biomass"            "regional natural gas"        "elect_td_ind"                "wholesale gas"               "H2 enduse"                  
[12] "refined liquids industrial"  "oil-credits"                 "industrial energy use"       "industrial feedstocks"       "industrial processes"        "regional oil"                "renewable"                   "elect_td_trn"                "regional biomassOil"         "regional corn for ethanol"   "process heat cement"        
[23] "regional coal"               "regional oilcrop"           
# things that were NOT remapped as targets
unique((inputs_by_subsector_nonelec %>% filter(is.na(Target)))$sector) # look at this
character(0)
unique((inputs_by_subsector_nonelec %>% filter(is.na(Target)))$subsector)
character(0)
unique((inputs_by_subsector_nonelec %>% filter(is.na(Target)))$input)
character(0)
# check for unmatched sources
inputs_by_subsector_nonelec_unmatched_source <- inputs_by_subsector_nonelec %>% 
  filter(is.na(Source)) %>% 
  select(scenario, sector, subsector, input, Source, Target) %>% 
  unique

unique(inputs_by_subsector_nonelec_unmatched_source$input)
[1] "electricity domestic supply" "oil-credits"                 "renewable"                   "process heat cement"        
unmatched_sources <- c("electricity domestic supply", "oil-credits","renewable", "process heat cement","process heat dac")

if(! all(inputs_by_subsector_nonelec_unmatched_source$input %in% unmatched_sources )){
  unmatched <- setdiff(inputs_by_subsector_nonelec_unmatched_source$input, unmatched_sources)
  stop(paste0("Unmatched Sources in inputs by subsector nonelec. Check Source mapping file against gcam data: ", paste(unmatched, collapse = ' - ')))
}
# check for unmatched targets
inputs_by_subsector_nonelec_unmatched_target <- inputs_by_subsector_nonelec %>% 
  filter(is.na(Target)) %>% 
  select(scenario, sector, subsector, input, Source, Target) %>% 
  unique

unique(inputs_by_subsector_nonelec_unmatched_target$sector)
character(0)
unmatched_targets <- c("H2 central production", 
                       "H2 liquid truck",
                       "H2 pipeline",
                       "H2 wholesale delivery" #all intermediate hydrogen markets that are double counting - only want H2 industrial and H2 MHDV
                       )
if(! all(inputs_by_subsector_nonelec_unmatched_target$sector %in% unmatched_targets)){
  unmatched <- setdiff(inputs_by_subsector_nonelec_unmatched_target$sector, unmatched_targets)
  stop(paste0("Unmatched Sources in inputs by subsector nonelec. Check Source mapping file against gcam data: ", paste(unmatched, collapse = ' - ')))
}

Get other flows such as gas processing and backup electricity

gas_processing_flows <- inputByTechUSA %>%
  filter(sector == "gas processing") %>%
  left_join(source_mapping, by = "input") %>%
  left_join(target_mapping, by = "sector") %>%
  group_by(scenario, Units, year, Source, Target) %>%
  summarize(value = sum(value)) %>%
  ungroup()
`summarise()` has grouped output by 'scenario', 'Units', 'year', 'Source'. You can override using the `.groups` argument.
backup <- inputByTechUSA %>%
  filter(sector %in% c("backup_electricity", "csp_backup")) %>%
  left_join(source_mapping, by = "input") %>%
  left_join(target_mapping, by = "sector") %>%
  group_by(scenario, Units, year, Source, Target) %>%
  summarize(value = sum(value)) %>%
  ungroup()
`summarise()` has grouped output by 'scenario', 'Units', 'year', 'Source'. You can override using the `.groups` argument.

Electricity

elec_energy_by_subsector <- elecEnergyInputBySubsector %>% 
  filter(Units == 'EJ') %>%
  filter(!input %in% c('backup_electricity', 'csp_backup'),
         !subsector %in% c("nuclear", "geothermal")) %>% #don't want to double count electricity from backup, and nuclear and geothermal are reported from output
  left_join(target_mapping, by = 'sector') %>% 
  left_join(source_mapping, by = 'input') 

#hydropower is only available as an output. In the "direct equivalent" reporting convention used here, input = output
hydro_power <- elecEnergyInputBySubsector %>%
  filter(subsector == 'hydro') %>%
  mutate(Source = 'Hydropower',
         Target = 'Electricity')

# nuclear's reported thermal inputs assume a 3:1 conversion, so for "direct equivalent" reporting we use the output
nuclear <- elecEnergyInputBySubsector %>%
  filter(subsector == 'nuclear') %>%
  mutate(Source = 'Nuclear',
         Target = 'Electricity') 

# geothermal's reported thermal inputs assume a 10:1 conversion, so for "direct equivalent" reporting we use the output
geothermal <- elecEnergyInputBySubsector %>%
  filter(subsector == 'geothermal') %>%
  mutate(Source = 'Geothermal',
         Target = 'Electricity')
# put everything together
all_energy <- inputs_by_subsector_nonelec %>% 
  bind_rows(gas_processing_flows) %>% 
  bind_rows(backup) %>%
  bind_rows(elec_energy_by_subsector) %>% 
  bind_rows(hydro_power) %>% 
  bind_rows(nuclear) %>%
  bind_rows(geothermal)

Source_Target_all <- all_energy %>% 
  group_by(scenario, Units, Source, Target, year) %>%
  summarise(value = sum(value))  %>% 
  filter( Source != Target) %>% 
  filter( Target != 'Biomass') %>% 
  ungroup() 
`summarise()` has grouped output by 'scenario', 'Units', 'Source', 'Target'. You can override using the `.groups` argument.
datatable(Source_Target_all, filter = 'top', rownames = FALSE)

Plotting

scenario_name <- "rcp45cooler_ssp3"
plot_scenario_name <- 'RCP 4.5 Cooler SSP3'

select_year <- '2050'
gcam_data_unit <- 'EJ'

# sankey formatting
link_alpha <- .5

# source/target mapping

node_mapping_in <- node_mapping

# GCAM data
gcam_data <- Source_Target_all %>% 
  filter(scenario == scenario_name) %>% filter( year == select_year) %>% select(-scenario)

all_links <- c(gcam_data$Source, gcam_data$Target) %>% unique

node_mapping <- node_mapping_in %>% filter(label %in% all_links)

node_mapping$node <- 0:(nrow(node_mapping)-1)

# process node data
print('Process Node Data')
[1] "Process Node Data"
links_data <- gcam_data %>% 
  select(Source, Target, value) %>% 
  mutate(Target = ifelse(str_detect(Target, 'Ind'), 'Industry', Target)) %>% 
  group_by(Source, Target) %>% 
  summarize(value = sum(value)) %>% 
  ungroup() %>% 
  rename(Source_label = Source,
         Target_label = Target) %>% 
  left_join(node_mapping %>% select(label, node), by = c('Source_label' = 'label')) %>% 
  rename(Source_node = node) %>% 
  left_join(node_mapping %>% select(label, node), by = c('Target_label' = 'label')) %>% 
  rename(Target_node = node) %>% 
  left_join(node_mapping %>% select(label, stage, hex, color_name), by = c('Source_label' = 'label')) %>% 
  mutate(rgb = apply(FUN = paste, MARGIN = 2, X = col2rgb(hex), collapse = ',')) %>% 
  mutate(rgba = paste0('rgba(', rgb, ', ', link_alpha,')')) %>% 
  mutate(link_label = paste(Source_label, round(value, digits = 1),'EJ')) %>% 
  filter(value>0) %>% 
  arrange(Source_node)
`summarise()` has grouped output by 'Source'. You can override using the `.groups` argument.
datatable(links_data, filter = 'top', rownames = FALSE, options = list(pageLength = 10, scrollX = TRUE))
# process node percent labels

# source
source_sum <- links_data %>% 
  select(Source_label, value) %>% 
  left_join(node_mapping %>% select(label, stage), by = c('Source_label' = 'label')) %>% 
  rename(label=Source_label) %>% 
  filter(tolower(stage) == 'source') %>% 
  group_by(label, stage) %>% 
  summarize(node_sum = sum(value))
`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
source_total <- source_sum %>% 
  pull(node_sum) %>% sum

source_percent <- source_sum %>% 
  mutate(percent = node_sum/source_total*100) %>% 
  left_join(node_mapping) %>% 
  arrange(node) %>% 
  mutate(x = .01) %>% 
  mutate(csum_norm = source_total)
Joining with `by = join_by(label, stage)`
source_percent$csum <- cumsum(source_percent$node_sum)
source_percent$start <- lag(source_percent$csum)

# target
target_sum <- links_data %>% 
  select(Target_label, value) %>% 
  left_join(node_mapping %>% select(label, stage), by = c('Target_label' = 'label')) %>% 
  rename(label=Target_label) %>% 
  filter(stage == 'target') %>% 
  group_by(label, stage) %>% 
  summarize(node_sum = sum(value))
`summarise()` has grouped output by 'label'. You can override using the `.groups` argument.
target_total <- target_sum %>% 
  pull(node_sum) %>% sum

target_percent <- target_sum %>% 
  mutate(percent = node_sum/target_total*100) %>% 
  left_join(node_mapping) %>% 
  arrange(node) %>% 
  mutate(x = .95) %>% 
  mutate(csum_norm = target_total)
Joining with `by = join_by(label, stage)`
target_percent$csum <- cumsum(target_percent$node_sum)
target_percent$start <- lag(target_percent$csum)

# Intermediate Carriers Flows in
intermediate_nodes <- node_mapping %>% filter(stage == 'mid') %>% pull(label)
 intermediate_flows_in_total <- links_data %>%
   filter(Target_label %in% intermediate_nodes) %>% 
   group_by(Target_label) %>% 
   summarize(node_sum = sum(value))
 
 intermediate_percent <- intermediate_flows_in_total %>% 
   rename(label = Target_label) %>% 
   mutate(stage = 'mid') %>% 
   mutate(percent =node_sum/source_total*100) %>% 
   left_join(node_mapping)
Joining with `by = join_by(label, stage)`
 
 intermediate_total <- intermediate_percent %>% pull(node_sum) %>% sum
 
  intermediate_flows_out_total <- links_data %>%
   filter(Source_label %in% intermediate_nodes) %>% 
   group_by(Source_label) %>% 
   summarize(value = sum(value))
  
# process node locations 

# final node info
nodes_data <- bind_rows(source_percent, intermediate_percent, target_percent) %>%
  arrange(node)%>%
  replace_na(list(start = 0)) %>% 
  mutate(mid_point = (start+csum)/2) %>% 
  mutate(y = mid_point/csum_norm) %>% 
  mutate(y = ifelse(label == 'Gas', 0.3,
                    ifelse(label == 'Liquid Fuels', 0.2,
                    ifelse(label == 'Electricity', 0.6,
                    ifelse(label == 'Hydrogen',0.9,y))))) %>% 
  mutate(x = ifelse(label == 'Gas', 0.35,
                    ifelse(label == 'Liquid Fuels', 0.5,
                           ifelse(label == 'Electricity', 0.6,
                                  ifelse(label == 'Hydrogen',0.7,x))))) %>%
  mutate(node_label = ifelse(is.na(node_sum), label, 
                               paste0(label, ' ',round(node_sum, digits = 1) , gcam_data_unit, 
                                      ' ', round(percent, digits = 1),'%'))) 
  

# Check that Source and Targets in Links are in the node mapping

  if( any(is.na(links_data$Source_node)) ) stop("Check Source number mapping - NA's")
  if( any(is.na(links_data$Target_node)) ) stop("Check Target number mapping - NA's")
  
datatable(nodes_data, filter = 'top', rownames = FALSE, options = list(pageLength = 20, scrollX = TRUE))
# save files for Kendall
write_csv(Source_Target_all, paste0("../", data_dir, 'allenergy_source_target.csv'))
write_csv(nodes_data, paste0("../", data_dir, 'allenergy_nodes_data.csv'))
write_csv(links_data, paste0("../", data_dir, 'allenergy_links_data.csv'))
# plot sankey
sankey_figure <- plot_ly( 
      type = "sankey",
      # arrangement = "snap",
      domain = list(x =  c(0,1),y =  c(0,1)),
      orientation = "h",
      valueformat = ".0f",
      valuesuffix = gcam_data_unit,

# Nodes  
      node = list( label = nodes_data %>% pull(node_label),
                   color = nodes_data %>% pull(hex),
                   x = nodes_data %>% pull(x),
                   y = nodes_data %>% pull(y),
                   pad = 3,
                   thickness = 15,
                   line = list(color = "black",width = 0.5)),
  
# Links
      link = list(source = links_data$Source_node,
                  target = links_data$Target_node,
                  value =  links_data$value,
                  color =  links_data$rgba)
) 

# add Formatting
plot_title <- paste0('Energy - ', plot_scenario_name, ' - ',select_year)
sankey_figure <- sankey_figure %>% layout(
  title = plot_title,
  font = list(size = 11),
  xaxis = list(showgrid = F, zeroline = F),
  yaxis = list(showgrid = F, zeroline = F))

sankey_figure
NA
NA
LS0tDQp0aXRsZTogIkVuZXJneSBGbG93cyBmcm9tIHRoZSBJTTMgR0NBTS1VU0EgU2NlbmFyaW9zIg0KYXV0aG9yOiAiSGFzc2FuIE5pYXppIChoYXNzYW4ubmlhemlAcG5ubC5nb3YpIHwgQWRhcHRlZCBmcm9tIHRoZSB3b3JrIG9mIFJhY2hlbCBIb2VzbHkiDQpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICAjIHRvY19mbG9hdDogVFJVRQ0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBieSBkZWZhdWx0IGNvbGxhcHNlL2hpZGUgdGhlIGNvZGUNCiMga25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBGQUxTRSkNCiMgc2V0IHdvcmtpbmcgZGlyZWN0b3J5IHRvIG9uZSBmb2xkZXIgdXANCnNldHdkKCIuLi8iKQ0KIyBnZXR3ZCgpDQpzb3VyY2UoIi4vUi9mdW5jdGlvbnMuUiIpDQpgYGANCg0KIyMjIEZvciB0aGUgSVdQUiBRMSBVcGRhdGUgKEphbiAyMDI1KQ0KDQotICAgR29hbDogcGxvdCBhbiBhbGwgZW5lcmd5IHNhbmtleSBmb3IgUTEgdXBkYXRlIG9mIHRoZSBFVy1GbG93cyBwcm9qZWN0DQoNCiMjIyBRdWVyeSBhbmQgbG9hZCBJTTMgZGF0YQ0KDQpSdW4gdGhpcyBgcXVlcnlfaW0zX3NjZW4oImVuZXJneSIpYCBvbmx5IG9uY2UgdG8gcXVlcnkgZnJvbSByZW1vdGUgSU0zIGRhdGFiYXNlcy4gT25jZSBhIGAuZGF0YCBmaWxlIGlzIGNyZWF0ZWQsIHdlIGNhbiBsb2FkIHRoZSBleGlzdGluZyBwcm9qZWN0IGRhdGEgYnkgYGxvYWRQcm9qZWN0KHByb2ogPSAiaW0zc2Nlbl9lbmVyZ3kuZGF0IilgLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBxdWVyeSB0aGUgZGF0YQ0KIyBpbTNfZW5lcmd5IDwtIHF1ZXJ5X2ltM19zY2VuKCJlbmVyZ3kiKQ0KYGBgDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9DQojIGxvYWQgdGhlIGRhdGENCmltM19lbmVyZ3kgPC0gbG9hZFByb2plY3QocHJvaiA9IHBhc3RlMCgiLi4vIiwgZGF0YV9kaXIsICJpbTNzY2VuX2VuZXJneS5kYXQiKSkNCmBgYA0KDQpgYGB7cn0NCiMgc2NlbmFyaW9zIGFuZCBxdWVyaWVzIA0KbGlzdFNjZW5hcmlvcyhpbTNfZW5lcmd5KQ0KbGlzdFF1ZXJpZXMoaW0zX2VuZXJneSkNCmBgYA0KDQoNCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9DQojIG1hcHBpbmdzIA0Kc291cmNlX21hcHBpbmcgPC0gcmVhZF9jc3YocGFzdGUwKCIuLi8iLCBkYXRhX2RpciwgIm1hcHBpbmdzL3NvdXJjZV9tYXBwaW5nX2VuLmNzdiIpKQ0KdGFyZ2V0X21hcHBpbmcgPC0gcmVhZF9jc3YocGFzdGUwKCIuLi8iLCBkYXRhX2RpciwgIm1hcHBpbmdzL3RhcmdldF9tYXBwaW5nX2VuLmNzdiIpKQ0Kbm9kZV9tYXBwaW5nIDwtIHJlYWRfY3N2KHBhc3RlMCgiLi4vIiwgZGF0YV9kaXIsICJtYXBwaW5ncy9ub2RlX21hcHBpbmdfZW4uY3N2IikpIA0KYGBgDQoNCg0KIyMjIEVuZXJneSBTYW5rZXkNCg0KYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0NCiMgZ2V0IHF1ZXJpZXMgDQppbnB1dEJ5VGVjaFVTQSA8LSBnZXRRdWVyeShpbTNfZW5lcmd5LCAiVVNBIGlucHV0cyBieSB0ZWNoIikgDQpvdXRwdXRCeVRlY2hVU0EgPC0gZ2V0UXVlcnkoaW0zX2VuZXJneSwgIlVTQSBvdXRwdXRzIGJ5IHRlY2giKQ0KDQppbnB1dEJ5U3Vic2VjdG9yTm9uRWxlYyA8LSBnZXRRdWVyeShpbTNfZW5lcmd5LCAnaW5wdXRzIGJ5IHN1YnNlY3RvciAobm9uLWVsZWN0cmljKScpDQplbGVjRW5lcmd5SW5wdXRCeVN1YnNlY3RvciA8LSBnZXRRdWVyeShpbTNfZW5lcmd5LCAnZWxlYyBlbmVyZ3kgaW5wdXQgYnkgc3Vic2VjdG9yJykgJT4lIGZpbHRlcihVbml0cyA9PSAiRUoiKSAjIGluIGNhc2Ugbm8gZmlsdGVyaW5nIG9mIEVMRUNfUlBTIGNyZWRpdHMNCmVsZWNHZW5CeVN1YnNlY3RvciA8LSBnZXRRdWVyeShpbTNfZW5lcmd5LCAnZWxlYyBnZW4gYnkgc3Vic2VjdG9yJykgJT4lIGZpbHRlcihVbml0cyA9PSAiRUoiKSAjIGluIGNhc2Ugbm8gZmlsdGVyaW5nIG9mIEVMRUNfUlBTIGNyZWRpdHMNCm5hdEdhc091dHB1dHMgPC0gZ2V0UXVlcnkoaW0zX2VuZXJneSwgJ1VTQSByZWdpb25hbCBuYXR1cmFsIGdhcyBvdXRwdXRzJykNCg0KYGBgDQoNCmBgYHtyLCBtZXNzc2FnZSA9IFQsIHdhcm5pbmcgPSBGQUxTRX0NCmRhdGF0YWJsZXNfZW5lcmd5IDwtIGxpc3QoDQogICJpbnB1dEJ5VGVjaFVTQSIgPSBpbnB1dEJ5VGVjaFVTQSwNCiAgIm91dHB1dEJ5VGVjaFVTQSIgPSBvdXRwdXRCeVRlY2hVU0EsDQogICJpbnB1dEJ5U3Vic2VjdG9yTm9uRWxlYyIgPSBpbnB1dEJ5U3Vic2VjdG9yTm9uRWxlYywNCiAgImVsZWNFbmVyZ3lJbnB1dEJ5U3Vic2VjdG9yIiA9IGVsZWNFbmVyZ3lJbnB1dEJ5U3Vic2VjdG9yLA0KICAiZWxlY0dlbkJ5U3Vic2VjdG9yIiA9IGVsZWNHZW5CeVN1YnNlY3RvciwNCiAgIm5hdEdhc091dHB1dHMiID0gbmF0R2FzT3V0cHV0cw0KKQ0KDQojIHByaW50IGNvbHVtbiBuYW1lcyBvZiBlYWNoIGRhdGF0YWJsZQ0KbGFwcGx5KGRhdGF0YWJsZXNfZW5lcmd5LCBmdW5jdGlvbih4KSBjb2xuYW1lcyh4KSkNCg0KIyBwcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgZWFjaCBkYXRhdGFibGUNCmxhcHBseShkYXRhdGFibGVzX2VuZXJneSwgZnVuY3Rpb24oeCkgKHgpKQ0KYGBgDQoNCkxldCdzIHByb2Nlc3MgZWFjaCBwaWVjZSB0byBwcmVwYXJlIHRoZSBmb3JtYXQgb2Y6IHNjZW5hcmlvLCBzb3VyY2UsIHRhcmdldCwgeWVhciwgdmFsdWUuIFNjZW5hcmlvIGFuZCB5ZWFyIGNvdWxkIGJlIGZpbHRlcmVkIGZvciBlYWNoIFNhbmtleS4NCg0KIyMjIyBOb24tZWxlY3RyaWNpdHkgDQoNCg0KYGBge3IgZmlnLndpZHRoPTgsIHdhcm5pbmc9RkFMU0V9DQojIG1hcCBub24gZWxlY3RyaWNpdHkgZW5lcmd5IGZsb3dzIHRvIG1ham9yIGFnZ3JlZ2F0ZWQgY2F0ZWdvcmllcyBiYXNlZCBvbiB0aGUgbWFwcGluZyBmaWxlIA0KDQoNCmlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlYyA8LSBpbnB1dEJ5U3Vic2VjdG9yTm9uRWxlYyAlPiUgDQogIGZpbHRlcihVbml0cyA9PSAnRUonKSAlPiUNCiAgZmlsdGVyKCFpbnB1dCAlaW4lIGMoJ3JlZ2lvbmFsIGNvcm4nLCAncmVnaW9uYWwgc295YmVhbicpKSAlPiUgDQogICMgYWdncmVnYXRlIGFsbCBtb250aGx5X2RheSBjb21iaW5hdGlvbnMgdG8gb25lIGNhdGVnb3J5IGUuZy4sIGVsZWN0cmljaXR5IGRvbWVzdGljIHN1cHBseV9Ob3ZfZGF5IHRvIGVsZWN0cmljaXR5IGRvbWVzdGljIHN1cHBseQ0KICByZW1vdmVfbW9udGhfZGF5X25pZ2h0X3N1cGVycGVhaygic2VjdG9yIikgJT4lIHJlbW92ZV9tb250aF9kYXlfbmlnaHRfc3VwZXJwZWFrKCJpbnB1dCIpICU+JQ0KICBsZWZ0X2pvaW4oc291cmNlX21hcHBpbmcsIGJ5ID0gJ2lucHV0JykgJT4lDQogIGxlZnRfam9pbih0YXJnZXRfbWFwcGluZywgYnkgPSAnc2VjdG9yJykNCg0KIyBOb3RlIHRoZXJlIGFyZSBOQXMgaW4gdGhlIG91dHB1dCBkdWUgdG8gbWlzc2luZyBtYXBwaW5ncyBvciBzZWN0b3JzIHRoYXQgYXJlDQojIG5vdCBzdXBwb3NlZCB0byBiZSB0YXJnZXRzIGFuZCBpbnB1dHMgdGhhdCBhcmUgbm90IHN1cHBvc2VkIyB0byBiZSBzb3VyY2VzDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgdGhpbmdzIHRoYXQgd2VyZSByZW1hcHBlZCBhcyBzb3VyY2VzDQp1bmlxdWUoKGlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlYyAlPiUgZmlsdGVyKCFpcy5uYShTb3VyY2UpKSkkc2VjdG9yKQ0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcighaXMubmEoU291cmNlKSkpJHN1YnNlY3RvcikNCnVuaXF1ZSgoaW5wdXRzX2J5X3N1YnNlY3Rvcl9ub25lbGVjICU+JSBmaWx0ZXIoIWlzLm5hKFNvdXJjZSkpKSRpbnB1dCkgIyBsb29rIGF0IHRoaXMNCmBgYA0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgdGhpbmdzIHRoZXJlIHdlcmUgTk9UIG1hcHBlZCBhcyBzb3VyY2VzIA0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcihpcy5uYShTb3VyY2UpKSkkc2VjdG9yKQ0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcihpcy5uYShTb3VyY2UpKSkkc3Vic2VjdG9yKQ0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcihpcy5uYShTb3VyY2UpKSkkaW5wdXQpICMgbG9vayBhdCB0aGlzIA0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTgsIHdhcm5pbmc9RkFMU0V9DQojIHRoaW5ncyB0aGF0IHdlcmUgcmVtYXBwZWQgYXMgdGFyZ2V0cw0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcighaXMubmEoVGFyZ2V0KSkpJHNlY3RvcikgIyBsb29rIGF0IHRoaXMNCnVuaXF1ZSgoaW5wdXRzX2J5X3N1YnNlY3Rvcl9ub25lbGVjICU+JSBmaWx0ZXIoIWlzLm5hKFRhcmdldCkpKSRzdWJzZWN0b3IpDQp1bmlxdWUoKGlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlYyAlPiUgZmlsdGVyKCFpcy5uYShUYXJnZXQpKSkkaW5wdXQpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgdGhpbmdzIHRoYXQgd2VyZSBOT1QgcmVtYXBwZWQgYXMgdGFyZ2V0cw0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcihpcy5uYShUYXJnZXQpKSkkc2VjdG9yKSAjIGxvb2sgYXQgdGhpcw0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcihpcy5uYShUYXJnZXQpKSkkc3Vic2VjdG9yKQ0KdW5pcXVlKChpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIGZpbHRlcihpcy5uYShUYXJnZXQpKSkkaW5wdXQpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgY2hlY2sgZm9yIHVubWF0Y2hlZCBzb3VyY2VzDQppbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWNfdW5tYXRjaGVkX3NvdXJjZSA8LSBpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWMgJT4lIA0KICBmaWx0ZXIoaXMubmEoU291cmNlKSkgJT4lIA0KICBzZWxlY3Qoc2NlbmFyaW8sIHNlY3Rvciwgc3Vic2VjdG9yLCBpbnB1dCwgU291cmNlLCBUYXJnZXQpICU+JSANCiAgdW5pcXVlDQoNCnVuaXF1ZShpbnB1dHNfYnlfc3Vic2VjdG9yX25vbmVsZWNfdW5tYXRjaGVkX3NvdXJjZSRpbnB1dCkNCg0KdW5tYXRjaGVkX3NvdXJjZXMgPC0gYygiZWxlY3RyaWNpdHkgZG9tZXN0aWMgc3VwcGx5IiwgIm9pbC1jcmVkaXRzIiwicmVuZXdhYmxlIiwgInByb2Nlc3MgaGVhdCBjZW1lbnQiLCJwcm9jZXNzIGhlYXQgZGFjIikNCg0KaWYoISBhbGwoaW5wdXRzX2J5X3N1YnNlY3Rvcl9ub25lbGVjX3VubWF0Y2hlZF9zb3VyY2UkaW5wdXQgJWluJSB1bm1hdGNoZWRfc291cmNlcyApKXsNCiAgdW5tYXRjaGVkIDwtIHNldGRpZmYoaW5wdXRzX2J5X3N1YnNlY3Rvcl9ub25lbGVjX3VubWF0Y2hlZF9zb3VyY2UkaW5wdXQsIHVubWF0Y2hlZF9zb3VyY2VzKQ0KICBzdG9wKHBhc3RlMCgiVW5tYXRjaGVkIFNvdXJjZXMgaW4gaW5wdXRzIGJ5IHN1YnNlY3RvciBub25lbGVjLiBDaGVjayBTb3VyY2UgbWFwcGluZyBmaWxlIGFnYWluc3QgZ2NhbSBkYXRhOiAiLCBwYXN0ZSh1bm1hdGNoZWQsIGNvbGxhcHNlID0gJyAtICcpKSkNCn0NCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD04LCB3YXJuaW5nPUZBTFNFfQ0KIyBjaGVjayBmb3IgdW5tYXRjaGVkIHRhcmdldHMNCmlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlY191bm1hdGNoZWRfdGFyZ2V0IDwtIGlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlYyAlPiUgDQogIGZpbHRlcihpcy5uYShUYXJnZXQpKSAlPiUgDQogIHNlbGVjdChzY2VuYXJpbywgc2VjdG9yLCBzdWJzZWN0b3IsIGlucHV0LCBTb3VyY2UsIFRhcmdldCkgJT4lIA0KICB1bmlxdWUNCg0KdW5pcXVlKGlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlY191bm1hdGNoZWRfdGFyZ2V0JHNlY3RvcikNCg0KdW5tYXRjaGVkX3RhcmdldHMgPC0gYygiSDIgY2VudHJhbCBwcm9kdWN0aW9uIiwgDQogICAgICAgICAgICAgICAgICAgICAgICJIMiBsaXF1aWQgdHJ1Y2siLA0KICAgICAgICAgICAgICAgICAgICAgICAiSDIgcGlwZWxpbmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAiSDIgd2hvbGVzYWxlIGRlbGl2ZXJ5IiAjYWxsIGludGVybWVkaWF0ZSBoeWRyb2dlbiBtYXJrZXRzIHRoYXQgYXJlIGRvdWJsZSBjb3VudGluZyAtIG9ubHkgd2FudCBIMiBpbmR1c3RyaWFsIGFuZCBIMiBNSERWDQogICAgICAgICAgICAgICAgICAgICAgICkNCmlmKCEgYWxsKGlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlY191bm1hdGNoZWRfdGFyZ2V0JHNlY3RvciAlaW4lIHVubWF0Y2hlZF90YXJnZXRzKSl7DQogIHVubWF0Y2hlZCA8LSBzZXRkaWZmKGlucHV0c19ieV9zdWJzZWN0b3Jfbm9uZWxlY191bm1hdGNoZWRfdGFyZ2V0JHNlY3RvciwgdW5tYXRjaGVkX3RhcmdldHMpDQogIHN0b3AocGFzdGUwKCJVbm1hdGNoZWQgU291cmNlcyBpbiBpbnB1dHMgYnkgc3Vic2VjdG9yIG5vbmVsZWMuIENoZWNrIFNvdXJjZSBtYXBwaW5nIGZpbGUgYWdhaW5zdCBnY2FtIGRhdGE6ICIsIHBhc3RlKHVubWF0Y2hlZCwgY29sbGFwc2UgPSAnIC0gJykpKQ0KfQ0KDQoNCmBgYA0KDQpHZXQgb3RoZXIgZmxvd3Mgc3VjaCBhcyBnYXMgcHJvY2Vzc2luZyBhbmQgYmFja3VwIGVsZWN0cmljaXR5DQoNCmBgYHtyIGZpZy53aWR0aD04LCB3YXJuaW5nPUZBTFNFfQ0KZ2FzX3Byb2Nlc3NpbmdfZmxvd3MgPC0gaW5wdXRCeVRlY2hVU0EgJT4lDQogIGZpbHRlcihzZWN0b3IgPT0gImdhcyBwcm9jZXNzaW5nIikgJT4lDQogIGxlZnRfam9pbihzb3VyY2VfbWFwcGluZywgYnkgPSAiaW5wdXQiKSAlPiUNCiAgbGVmdF9qb2luKHRhcmdldF9tYXBwaW5nLCBieSA9ICJzZWN0b3IiKSAlPiUNCiAgZ3JvdXBfYnkoc2NlbmFyaW8sIFVuaXRzLCB5ZWFyLCBTb3VyY2UsIFRhcmdldCkgJT4lDQogIHN1bW1hcml6ZSh2YWx1ZSA9IHN1bSh2YWx1ZSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KYmFja3VwIDwtIGlucHV0QnlUZWNoVVNBICU+JQ0KICBmaWx0ZXIoc2VjdG9yICVpbiUgYygiYmFja3VwX2VsZWN0cmljaXR5IiwgImNzcF9iYWNrdXAiKSkgJT4lDQogIGxlZnRfam9pbihzb3VyY2VfbWFwcGluZywgYnkgPSAiaW5wdXQiKSAlPiUNCiAgbGVmdF9qb2luKHRhcmdldF9tYXBwaW5nLCBieSA9ICJzZWN0b3IiKSAlPiUNCiAgZ3JvdXBfYnkoc2NlbmFyaW8sIFVuaXRzLCB5ZWFyLCBTb3VyY2UsIFRhcmdldCkgJT4lDQogIHN1bW1hcml6ZSh2YWx1ZSA9IHN1bSh2YWx1ZSkpICU+JQ0KICB1bmdyb3VwKCkNCmBgYA0KDQojIyMjIEVsZWN0cmljaXR5DQoNCmBgYHtyIGZpZy53aWR0aD04LCB3YXJuaW5nPUZBTFNFfQ0KZWxlY19lbmVyZ3lfYnlfc3Vic2VjdG9yIDwtIGVsZWNFbmVyZ3lJbnB1dEJ5U3Vic2VjdG9yICU+JSANCiAgZmlsdGVyKFVuaXRzID09ICdFSicpICU+JQ0KICBmaWx0ZXIoIWlucHV0ICVpbiUgYygnYmFja3VwX2VsZWN0cmljaXR5JywgJ2NzcF9iYWNrdXAnKSwNCiAgICAgICAgICFzdWJzZWN0b3IgJWluJSBjKCJudWNsZWFyIiwgImdlb3RoZXJtYWwiKSkgJT4lICNkb24ndCB3YW50IHRvIGRvdWJsZSBjb3VudCBlbGVjdHJpY2l0eSBmcm9tIGJhY2t1cCwgYW5kIG51Y2xlYXIgYW5kIGdlb3RoZXJtYWwgYXJlIHJlcG9ydGVkIGZyb20gb3V0cHV0DQogIGxlZnRfam9pbih0YXJnZXRfbWFwcGluZywgYnkgPSAnc2VjdG9yJykgJT4lIA0KICBsZWZ0X2pvaW4oc291cmNlX21hcHBpbmcsIGJ5ID0gJ2lucHV0JykgDQoNCiNoeWRyb3Bvd2VyIGlzIG9ubHkgYXZhaWxhYmxlIGFzIGFuIG91dHB1dC4gSW4gdGhlICJkaXJlY3QgZXF1aXZhbGVudCIgcmVwb3J0aW5nIGNvbnZlbnRpb24gdXNlZCBoZXJlLCBpbnB1dCA9IG91dHB1dA0KaHlkcm9fcG93ZXIgPC0gZWxlY0VuZXJneUlucHV0QnlTdWJzZWN0b3IgJT4lDQogIGZpbHRlcihzdWJzZWN0b3IgPT0gJ2h5ZHJvJykgJT4lDQogIG11dGF0ZShTb3VyY2UgPSAnSHlkcm9wb3dlcicsDQogICAgICAgICBUYXJnZXQgPSAnRWxlY3RyaWNpdHknKQ0KDQojIG51Y2xlYXIncyByZXBvcnRlZCB0aGVybWFsIGlucHV0cyBhc3N1bWUgYSAzOjEgY29udmVyc2lvbiwgc28gZm9yICJkaXJlY3QgZXF1aXZhbGVudCIgcmVwb3J0aW5nIHdlIHVzZSB0aGUgb3V0cHV0DQpudWNsZWFyIDwtIGVsZWNFbmVyZ3lJbnB1dEJ5U3Vic2VjdG9yICU+JQ0KICBmaWx0ZXIoc3Vic2VjdG9yID09ICdudWNsZWFyJykgJT4lDQogIG11dGF0ZShTb3VyY2UgPSAnTnVjbGVhcicsDQogICAgICAgICBUYXJnZXQgPSAnRWxlY3RyaWNpdHknKSANCg0KIyBnZW90aGVybWFsJ3MgcmVwb3J0ZWQgdGhlcm1hbCBpbnB1dHMgYXNzdW1lIGEgMTA6MSBjb252ZXJzaW9uLCBzbyBmb3IgImRpcmVjdCBlcXVpdmFsZW50IiByZXBvcnRpbmcgd2UgdXNlIHRoZSBvdXRwdXQNCmdlb3RoZXJtYWwgPC0gZWxlY0VuZXJneUlucHV0QnlTdWJzZWN0b3IgJT4lDQogIGZpbHRlcihzdWJzZWN0b3IgPT0gJ2dlb3RoZXJtYWwnKSAlPiUNCiAgbXV0YXRlKFNvdXJjZSA9ICdHZW90aGVybWFsJywNCiAgICAgICAgIFRhcmdldCA9ICdFbGVjdHJpY2l0eScpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgcHV0IGV2ZXJ5dGhpbmcgdG9nZXRoZXINCmFsbF9lbmVyZ3kgPC0gaW5wdXRzX2J5X3N1YnNlY3Rvcl9ub25lbGVjICU+JSANCiAgYmluZF9yb3dzKGdhc19wcm9jZXNzaW5nX2Zsb3dzKSAlPiUgDQogIGJpbmRfcm93cyhiYWNrdXApICU+JQ0KICBiaW5kX3Jvd3MoZWxlY19lbmVyZ3lfYnlfc3Vic2VjdG9yKSAlPiUgDQogIGJpbmRfcm93cyhoeWRyb19wb3dlcikgJT4lIA0KICBiaW5kX3Jvd3MobnVjbGVhcikgJT4lDQogIGJpbmRfcm93cyhnZW90aGVybWFsKQ0KDQpTb3VyY2VfVGFyZ2V0X2FsbCA8LSBhbGxfZW5lcmd5ICU+JSANCiAgZ3JvdXBfYnkoc2NlbmFyaW8sIFVuaXRzLCBTb3VyY2UsIFRhcmdldCwgeWVhcikgJT4lDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSkpICAlPiUgDQogIGZpbHRlciggU291cmNlICE9IFRhcmdldCkgJT4lIA0KICBmaWx0ZXIoIFRhcmdldCAhPSAnQmlvbWFzcycpICU+JSANCiAgdW5ncm91cCgpIA0KDQpkYXRhdGFibGUoU291cmNlX1RhcmdldF9hbGwsIGZpbHRlciA9ICd0b3AnLCByb3duYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCiMjIyMgUGxvdHRpbmcgDQoNCmBgYHtyIGZpZy53aWR0aD04LCB3YXJuaW5nPUZBTFNFfQ0Kc2NlbmFyaW9fbmFtZSA8LSAicmNwNDVjb29sZXJfc3NwMyINCnBsb3Rfc2NlbmFyaW9fbmFtZSA8LSAnUkNQIDQuNSBDb29sZXIgU1NQMycNCg0Kc2VsZWN0X3llYXIgPC0gJzIwNTAnDQpnY2FtX2RhdGFfdW5pdCA8LSAnRUonDQoNCiMgc2Fua2V5IGZvcm1hdHRpbmcNCmxpbmtfYWxwaGEgPC0gLjUNCg0KIyBzb3VyY2UvdGFyZ2V0IG1hcHBpbmcNCg0Kbm9kZV9tYXBwaW5nX2luIDwtIG5vZGVfbWFwcGluZw0KDQojIEdDQU0gZGF0YQ0KZ2NhbV9kYXRhIDwtIFNvdXJjZV9UYXJnZXRfYWxsICU+JSANCiAgZmlsdGVyKHNjZW5hcmlvID09IHNjZW5hcmlvX25hbWUpICU+JSBmaWx0ZXIoIHllYXIgPT0gc2VsZWN0X3llYXIpICU+JSBzZWxlY3QoLXNjZW5hcmlvKQ0KDQphbGxfbGlua3MgPC0gYyhnY2FtX2RhdGEkU291cmNlLCBnY2FtX2RhdGEkVGFyZ2V0KSAlPiUgdW5pcXVlDQoNCm5vZGVfbWFwcGluZyA8LSBub2RlX21hcHBpbmdfaW4gJT4lIGZpbHRlcihsYWJlbCAlaW4lIGFsbF9saW5rcykNCg0Kbm9kZV9tYXBwaW5nJG5vZGUgPC0gMDoobnJvdyhub2RlX21hcHBpbmcpLTEpDQoNCiMgcHJvY2VzcyBub2RlIGRhdGENCmxpbmtzX2RhdGEgPC0gZ2NhbV9kYXRhICU+JSANCiAgc2VsZWN0KFNvdXJjZSwgVGFyZ2V0LCB2YWx1ZSkgJT4lIA0KICBtdXRhdGUoVGFyZ2V0ID0gaWZlbHNlKHN0cl9kZXRlY3QoVGFyZ2V0LCAnSW5kJyksICdJbmR1c3RyeScsIFRhcmdldCkpICU+JSANCiAgZ3JvdXBfYnkoU291cmNlLCBUYXJnZXQpICU+JSANCiAgc3VtbWFyaXplKHZhbHVlID0gc3VtKHZhbHVlKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICByZW5hbWUoU291cmNlX2xhYmVsID0gU291cmNlLA0KICAgICAgICAgVGFyZ2V0X2xhYmVsID0gVGFyZ2V0KSAlPiUgDQogIGxlZnRfam9pbihub2RlX21hcHBpbmcgJT4lIHNlbGVjdChsYWJlbCwgbm9kZSksIGJ5ID0gYygnU291cmNlX2xhYmVsJyA9ICdsYWJlbCcpKSAlPiUgDQogIHJlbmFtZShTb3VyY2Vfbm9kZSA9IG5vZGUpICU+JSANCiAgbGVmdF9qb2luKG5vZGVfbWFwcGluZyAlPiUgc2VsZWN0KGxhYmVsLCBub2RlKSwgYnkgPSBjKCdUYXJnZXRfbGFiZWwnID0gJ2xhYmVsJykpICU+JSANCiAgcmVuYW1lKFRhcmdldF9ub2RlID0gbm9kZSkgJT4lIA0KICBsZWZ0X2pvaW4obm9kZV9tYXBwaW5nICU+JSBzZWxlY3QobGFiZWwsIHN0YWdlLCBoZXgsIGNvbG9yX25hbWUpLCBieSA9IGMoJ1NvdXJjZV9sYWJlbCcgPSAnbGFiZWwnKSkgJT4lIA0KICBtdXRhdGUocmdiID0gYXBwbHkoRlVOID0gcGFzdGUsIE1BUkdJTiA9IDIsIFggPSBjb2wycmdiKGhleCksIGNvbGxhcHNlID0gJywnKSkgJT4lIA0KICBtdXRhdGUocmdiYSA9IHBhc3RlMCgncmdiYSgnLCByZ2IsICcsICcsIGxpbmtfYWxwaGEsJyknKSkgJT4lIA0KICBtdXRhdGUobGlua19sYWJlbCA9IHBhc3RlKFNvdXJjZV9sYWJlbCwgcm91bmQodmFsdWUsIGRpZ2l0cyA9IDEpLCdFSicpKSAlPiUgDQogIGZpbHRlcih2YWx1ZT4wKSAlPiUgDQogIGFycmFuZ2UoU291cmNlX25vZGUpDQoNCmRhdGF0YWJsZShsaW5rc19kYXRhLCBmaWx0ZXIgPSAndG9wJywgcm93bmFtZXMgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwLCBzY3JvbGxYID0gVFJVRSkpDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgcHJvY2VzcyBub2RlIHBlcmNlbnQgbGFiZWxzDQoNCiMgc291cmNlDQpzb3VyY2Vfc3VtIDwtIGxpbmtzX2RhdGEgJT4lIA0KICBzZWxlY3QoU291cmNlX2xhYmVsLCB2YWx1ZSkgJT4lIA0KICBsZWZ0X2pvaW4obm9kZV9tYXBwaW5nICU+JSBzZWxlY3QobGFiZWwsIHN0YWdlKSwgYnkgPSBjKCdTb3VyY2VfbGFiZWwnID0gJ2xhYmVsJykpICU+JSANCiAgcmVuYW1lKGxhYmVsPVNvdXJjZV9sYWJlbCkgJT4lIA0KICBmaWx0ZXIodG9sb3dlcihzdGFnZSkgPT0gJ3NvdXJjZScpICU+JSANCiAgZ3JvdXBfYnkobGFiZWwsIHN0YWdlKSAlPiUgDQogIHN1bW1hcml6ZShub2RlX3N1bSA9IHN1bSh2YWx1ZSkpDQoNCnNvdXJjZV90b3RhbCA8LSBzb3VyY2Vfc3VtICU+JSANCiAgcHVsbChub2RlX3N1bSkgJT4lIHN1bQ0KDQpzb3VyY2VfcGVyY2VudCA8LSBzb3VyY2Vfc3VtICU+JSANCiAgbXV0YXRlKHBlcmNlbnQgPSBub2RlX3N1bS9zb3VyY2VfdG90YWwqMTAwKSAlPiUgDQogIGxlZnRfam9pbihub2RlX21hcHBpbmcpICU+JSANCiAgYXJyYW5nZShub2RlKSAlPiUgDQogIG11dGF0ZSh4ID0gLjAxKSAlPiUgDQogIG11dGF0ZShjc3VtX25vcm0gPSBzb3VyY2VfdG90YWwpDQpzb3VyY2VfcGVyY2VudCRjc3VtIDwtIGN1bXN1bShzb3VyY2VfcGVyY2VudCRub2RlX3N1bSkNCnNvdXJjZV9wZXJjZW50JHN0YXJ0IDwtIGxhZyhzb3VyY2VfcGVyY2VudCRjc3VtKQ0KDQojIHRhcmdldA0KdGFyZ2V0X3N1bSA8LSBsaW5rc19kYXRhICU+JSANCiAgc2VsZWN0KFRhcmdldF9sYWJlbCwgdmFsdWUpICU+JSANCiAgbGVmdF9qb2luKG5vZGVfbWFwcGluZyAlPiUgc2VsZWN0KGxhYmVsLCBzdGFnZSksIGJ5ID0gYygnVGFyZ2V0X2xhYmVsJyA9ICdsYWJlbCcpKSAlPiUgDQogIHJlbmFtZShsYWJlbD1UYXJnZXRfbGFiZWwpICU+JSANCiAgZmlsdGVyKHN0YWdlID09ICd0YXJnZXQnKSAlPiUgDQogIGdyb3VwX2J5KGxhYmVsLCBzdGFnZSkgJT4lIA0KICBzdW1tYXJpemUobm9kZV9zdW0gPSBzdW0odmFsdWUpKQ0KDQp0YXJnZXRfdG90YWwgPC0gdGFyZ2V0X3N1bSAlPiUgDQogIHB1bGwobm9kZV9zdW0pICU+JSBzdW0NCg0KdGFyZ2V0X3BlcmNlbnQgPC0gdGFyZ2V0X3N1bSAlPiUgDQogIG11dGF0ZShwZXJjZW50ID0gbm9kZV9zdW0vdGFyZ2V0X3RvdGFsKjEwMCkgJT4lIA0KICBsZWZ0X2pvaW4obm9kZV9tYXBwaW5nKSAlPiUgDQogIGFycmFuZ2Uobm9kZSkgJT4lIA0KICBtdXRhdGUoeCA9IC45NSkgJT4lIA0KICBtdXRhdGUoY3N1bV9ub3JtID0gdGFyZ2V0X3RvdGFsKQ0KdGFyZ2V0X3BlcmNlbnQkY3N1bSA8LSBjdW1zdW0odGFyZ2V0X3BlcmNlbnQkbm9kZV9zdW0pDQp0YXJnZXRfcGVyY2VudCRzdGFydCA8LSBsYWcodGFyZ2V0X3BlcmNlbnQkY3N1bSkNCg0KIyBJbnRlcm1lZGlhdGUgQ2FycmllcnMgRmxvd3MgaW4NCmludGVybWVkaWF0ZV9ub2RlcyA8LSBub2RlX21hcHBpbmcgJT4lIGZpbHRlcihzdGFnZSA9PSAnbWlkJykgJT4lIHB1bGwobGFiZWwpDQogaW50ZXJtZWRpYXRlX2Zsb3dzX2luX3RvdGFsIDwtIGxpbmtzX2RhdGEgJT4lDQogICBmaWx0ZXIoVGFyZ2V0X2xhYmVsICVpbiUgaW50ZXJtZWRpYXRlX25vZGVzKSAlPiUgDQogICBncm91cF9ieShUYXJnZXRfbGFiZWwpICU+JSANCiAgIHN1bW1hcml6ZShub2RlX3N1bSA9IHN1bSh2YWx1ZSkpDQogDQogaW50ZXJtZWRpYXRlX3BlcmNlbnQgPC0gaW50ZXJtZWRpYXRlX2Zsb3dzX2luX3RvdGFsICU+JSANCiAgIHJlbmFtZShsYWJlbCA9IFRhcmdldF9sYWJlbCkgJT4lIA0KICAgbXV0YXRlKHN0YWdlID0gJ21pZCcpICU+JSANCiAgIG11dGF0ZShwZXJjZW50ID1ub2RlX3N1bS9zb3VyY2VfdG90YWwqMTAwKSAlPiUgDQogICBsZWZ0X2pvaW4obm9kZV9tYXBwaW5nKQ0KIA0KIGludGVybWVkaWF0ZV90b3RhbCA8LSBpbnRlcm1lZGlhdGVfcGVyY2VudCAlPiUgcHVsbChub2RlX3N1bSkgJT4lIHN1bQ0KIA0KICBpbnRlcm1lZGlhdGVfZmxvd3Nfb3V0X3RvdGFsIDwtIGxpbmtzX2RhdGEgJT4lDQogICBmaWx0ZXIoU291cmNlX2xhYmVsICVpbiUgaW50ZXJtZWRpYXRlX25vZGVzKSAlPiUgDQogICBncm91cF9ieShTb3VyY2VfbGFiZWwpICU+JSANCiAgIHN1bW1hcml6ZSh2YWx1ZSA9IHN1bSh2YWx1ZSkpDQogIA0KYGBgDQoNCg0KYGBge3IgZmlnLndpZHRoPTgsIHdhcm5pbmc9RkFMU0V9DQojIHByb2Nlc3Mgbm9kZSBsb2NhdGlvbnMgDQoNCiMgZmluYWwgbm9kZSBpbmZvDQpub2Rlc19kYXRhIDwtIGJpbmRfcm93cyhzb3VyY2VfcGVyY2VudCwgaW50ZXJtZWRpYXRlX3BlcmNlbnQsIHRhcmdldF9wZXJjZW50KSAlPiUNCiAgYXJyYW5nZShub2RlKSU+JQ0KICByZXBsYWNlX25hKGxpc3Qoc3RhcnQgPSAwKSkgJT4lIA0KICBtdXRhdGUobWlkX3BvaW50ID0gKHN0YXJ0K2NzdW0pLzIpICU+JSANCiAgbXV0YXRlKHkgPSBtaWRfcG9pbnQvY3N1bV9ub3JtKSAlPiUgDQogIG11dGF0ZSh5ID0gaWZlbHNlKGxhYmVsID09ICdHYXMnLCAwLjMsDQogICAgICAgICAgICAgICAgICAgIGlmZWxzZShsYWJlbCA9PSAnTGlxdWlkIEZ1ZWxzJywgMC4yLA0KICAgICAgICAgICAgICAgICAgICBpZmVsc2UobGFiZWwgPT0gJ0VsZWN0cmljaXR5JywgMC42LA0KICAgICAgICAgICAgICAgICAgICBpZmVsc2UobGFiZWwgPT0gJ0h5ZHJvZ2VuJywwLjkseSkpKSkpICU+JSANCiAgbXV0YXRlKHggPSBpZmVsc2UobGFiZWwgPT0gJ0dhcycsIDAuMzUsDQogICAgICAgICAgICAgICAgICAgIGlmZWxzZShsYWJlbCA9PSAnTGlxdWlkIEZ1ZWxzJywgMC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGxhYmVsID09ICdFbGVjdHJpY2l0eScsIDAuNiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobGFiZWwgPT0gJ0h5ZHJvZ2VuJywwLjcseCkpKSkpICU+JQ0KICBtdXRhdGUobm9kZV9sYWJlbCA9IGlmZWxzZShpcy5uYShub2RlX3N1bSksIGxhYmVsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAobGFiZWwsICcgJyxyb3VuZChub2RlX3N1bSwgZGlnaXRzID0gMSkgLCBnY2FtX2RhdGFfdW5pdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcgJywgcm91bmQocGVyY2VudCwgZGlnaXRzID0gMSksJyUnKSkpIA0KICANCg0KIyBDaGVjayB0aGF0IFNvdXJjZSBhbmQgVGFyZ2V0cyBpbiBMaW5rcyBhcmUgaW4gdGhlIG5vZGUgbWFwcGluZw0KDQogIGlmKCBhbnkoaXMubmEobGlua3NfZGF0YSRTb3VyY2Vfbm9kZSkpICkgc3RvcCgiQ2hlY2sgU291cmNlIG51bWJlciBtYXBwaW5nIC0gTkEncyIpDQogIGlmKCBhbnkoaXMubmEobGlua3NfZGF0YSRUYXJnZXRfbm9kZSkpICkgc3RvcCgiQ2hlY2sgVGFyZ2V0IG51bWJlciBtYXBwaW5nIC0gTkEncyIpDQogIA0KZGF0YXRhYmxlKG5vZGVzX2RhdGEsIGZpbHRlciA9ICd0b3AnLCByb3duYW1lcyA9IEZBTFNFLCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gMjAsIHNjcm9sbFggPSBUUlVFKSkNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD04LCB3YXJuaW5nPUZBTFNFfQ0KIyBzYXZlIGZpbGVzIGZvciBLZW5kYWxsDQp3cml0ZV9jc3YoU291cmNlX1RhcmdldF9hbGwsIHBhc3RlMCgiLi4vIiwgZGF0YV9kaXIsICdhbGxlbmVyZ3lfc291cmNlX3RhcmdldC5jc3YnKSkNCndyaXRlX2Nzdihub2Rlc19kYXRhLCBwYXN0ZTAoIi4uLyIsIGRhdGFfZGlyLCAnYWxsZW5lcmd5X25vZGVzX2RhdGEuY3N2JykpDQp3cml0ZV9jc3YobGlua3NfZGF0YSwgcGFzdGUwKCIuLi8iLCBkYXRhX2RpciwgJ2FsbGVuZXJneV9saW5rc19kYXRhLmNzdicpKQ0KDQpgYGANCg0KDQpgYGB7ciBmaWcud2lkdGg9OCwgd2FybmluZz1GQUxTRX0NCiMgcGxvdCBzYW5rZXkNCnNhbmtleV9maWd1cmUgPC0gcGxvdF9seSggDQogICAgICB0eXBlID0gInNhbmtleSIsDQogICAgICAjIGFycmFuZ2VtZW50ID0gInNuYXAiLA0KICAgICAgZG9tYWluID0gbGlzdCh4ID0gIGMoMCwxKSx5ID0gIGMoMCwxKSksDQogICAgICBvcmllbnRhdGlvbiA9ICJoIiwNCiAgICAgIHZhbHVlZm9ybWF0ID0gIi4wZiIsDQogICAgICB2YWx1ZXN1ZmZpeCA9IGdjYW1fZGF0YV91bml0LA0KDQojIE5vZGVzICANCiAgICAgIG5vZGUgPSBsaXN0KCBsYWJlbCA9IG5vZGVzX2RhdGEgJT4lIHB1bGwobm9kZV9sYWJlbCksDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSBub2Rlc19kYXRhICU+JSBwdWxsKGhleCksDQogICAgICAgICAgICAgICAgICAgeCA9IG5vZGVzX2RhdGEgJT4lIHB1bGwoeCksDQogICAgICAgICAgICAgICAgICAgeSA9IG5vZGVzX2RhdGEgJT4lIHB1bGwoeSksDQogICAgICAgICAgICAgICAgICAgcGFkID0gMywNCiAgICAgICAgICAgICAgICAgICB0aGlja25lc3MgPSAxNSwNCiAgICAgICAgICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICJibGFjayIsd2lkdGggPSAwLjUpKSwNCiAgDQojIExpbmtzDQogICAgICBsaW5rID0gbGlzdChzb3VyY2UgPSBsaW5rc19kYXRhJFNvdXJjZV9ub2RlLA0KICAgICAgICAgICAgICAgICAgdGFyZ2V0ID0gbGlua3NfZGF0YSRUYXJnZXRfbm9kZSwNCiAgICAgICAgICAgICAgICAgIHZhbHVlID0gIGxpbmtzX2RhdGEkdmFsdWUsDQogICAgICAgICAgICAgICAgICBjb2xvciA9ICBsaW5rc19kYXRhJHJnYmEpDQopIA0KDQojIGFkZCBGb3JtYXR0aW5nDQpwbG90X3RpdGxlIDwtIHBhc3RlMCgnRW5lcmd5IC0gJywgcGxvdF9zY2VuYXJpb19uYW1lLCAnIC0gJyxzZWxlY3RfeWVhcikNCnNhbmtleV9maWd1cmUgPC0gc2Fua2V5X2ZpZ3VyZSAlPiUgbGF5b3V0KA0KICB0aXRsZSA9IHBsb3RfdGl0bGUsDQogIGZvbnQgPSBsaXN0KHNpemUgPSAxMSksDQogIHhheGlzID0gbGlzdChzaG93Z3JpZCA9IEYsIHplcm9saW5lID0gRiksDQogIHlheGlzID0gbGlzdChzaG93Z3JpZCA9IEYsIHplcm9saW5lID0gRikpDQoNCnNhbmtleV9maWd1cmUNCg0KDQpgYGANCg0KDQoNCmBgYHtyIGZpZy53aWR0aD04LCB3YXJuaW5nPUZBTFNFfQ0KDQoNCmBgYA0KDQoNCg0KDQoNCg0K